package notesDetection.MusicAnalyzer;

import java.util.*;

import notesDetection.Utilities.SegmentNotesInfo;
import notesDetection.wavPackage.WavLoader;

public class AnalyzeMusicFile {

	private String _inputFile;
	private WavLoader wavLoader;
	private double _windowSizeInSeconds;
	private double _advnaceFactor;
	private int _windowSizeInSamples;
	private int _advanceFactorInSamples;
	private int _numberOfSubSegments;

	private double[][] samplesBuffer1;
	private double[][] samplesBuffer2;
	
	private boolean currentUsedBuffer;
	
	public boolean keepRunning;
	
	public enum AnalysisPreferences
	{
		Normal,
		Notes,
		Compression
		
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args)
	{
		//	This is for DEBUG only. Run "NotesHavaGUI.java"
		final String fileToAnalyze = "D:\\SADNA\\GmChord.wav";
		AnalyzeMusicFile analyzer = new AnalyzeMusicFile(fileToAnalyze);
		analyzer.RunAnalysis(AnalysisPreferences.Normal, 0.5, 0.002, 5e-5, 0.3, 0.3, 1, 440.0);

	}
	
	public AnalyzeMusicFile(String inputFile)
	{
		_inputFile = inputFile;
		keepRunning = false;
		
		currentUsedBuffer = false; //	true == buffer1, false == buffer2
	}
	
	public List<SegmentNotesInfo> RunAnalysis(AnalysisPreferences preferences, double tau, double delta, double twoNormLowThreshold, double windowSizeInSeconds, double advanceFactorInSeconds, int subSegments, double aFrequencyHz)
	{	
		_windowSizeInSeconds = windowSizeInSeconds;
		_advnaceFactor = advanceFactorInSeconds;
		_numberOfSubSegments = subSegments;
		
		
		wavLoader = new WavLoader(_inputFile);
		if(!wavLoader.InitializeWavFile())
		{
			System.err.println("Unable to initialize file!");
			return null;
		}
		//	Number of frames which contain "windowSizeInSeconds" seconds.
		_windowSizeInSamples = (int)Math.round(_windowSizeInSeconds * wavLoader.getSampleFrequency());
		_advanceFactorInSamples = (int)Math.round(_advnaceFactor * wavLoader.getSampleFrequency());
		
		if(_advanceFactorInSamples > _windowSizeInSamples)
		{
			System.err.println("Invalid advance factor! advanceFactor must be <= window size!");
			return null;
		}
		
		samplesBuffer1 = new double[wavLoader.getNumberOfChannels()][_windowSizeInSamples];
		samplesBuffer2 = new double[wavLoader.getNumberOfChannels()][_windowSizeInSamples];
		
		currentUsedBuffer = true; //	true == buffer1, false == buffer2
		
		FrequencyFinder frequencyFinder = new FrequencyFinder(wavLoader.getSampleFrequency(), aFrequencyHz, tau, delta, twoNormLowThreshold);
		
		int iterationIndex = 0;
		
		List<SegmentNotesInfo> fileInfo = new ArrayList<SegmentNotesInfo>();
		int segmentId = 0;
		
		//	In case this analyzer is already running...
		//	Sleep for "x" time and then check again if it is finished.
		//	This was done to avoid overload, and not due to thread safety of some kind (as it's not really a perfect lock).
		while(keepRunning)
		{

			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				//	Why would this fail? STOP
				System.err.println(e.getMessage());
				return null;
			}
			
		}
		
		keepRunning = true;
		boolean hasMoreSegments = true;
		long firstUnavailableSample = 0;
		long currentAnalyzerSample = 0;
		
		//	Cyclic buffer might have been better, but...
		//	Using 2 buffers in size of MAX_WINDOW_SIZE we can copy the data needed to a temp "active buffer"
		double[][] activeBuffer = new double[wavLoader.getNumberOfChannels()][_windowSizeInSamples];
		
		while(hasMoreSegments && keepRunning)
		{
			if(currentAnalyzerSample + _windowSizeInSamples > firstUnavailableSample)
			{
				if(wavLoader.ReadFramesFromFile(getOldBuffer(), _windowSizeInSamples) != _windowSizeInSamples)
				{
					//	Reached the end
					break;
				}
				//	Old and new buffers are switched:
				currentUsedBuffer = !currentUsedBuffer;
				firstUnavailableSample += _windowSizeInSamples;
			}
			
			//	Copy relevant data to active buffer:
			
			//	A)	Advance factor is same as window size (NO window overlap)
			if(currentAnalyzerSample + _windowSizeInSamples == firstUnavailableSample)
			{
				for(int sampleIndex = 0; sampleIndex < _windowSizeInSamples; sampleIndex++)
				{
					activeBuffer[0][sampleIndex] = getNewBuffer()[0][sampleIndex];
				}
			}
			//	B)	Advance factor is smaller than window size.
			else
			{
				int oldBufferOffset = (int)currentAnalyzerSample % _windowSizeInSamples;
				for(int sampleIndex = oldBufferOffset; sampleIndex < _windowSizeInSamples; sampleIndex++)
				{
					activeBuffer[0][sampleIndex - oldBufferOffset] = getOldBuffer()[0][sampleIndex];
				}
				int newBufferMaxOffset = _windowSizeInSamples - oldBufferOffset;
				for(int sampleIndex = 0; sampleIndex < oldBufferOffset; sampleIndex++)
				{
					activeBuffer[0][sampleIndex + newBufferMaxOffset] = getNewBuffer()[0][sampleIndex];
				}
			}
			
			//	First, use SFT algorithm to detect cycle + heavy coefficients
			List<Integer> detectedNotes = frequencyFinder.FindSegmentTopCoefficients(activeBuffer[0], 0, _windowSizeInSamples - 1, preferences);
			//	Split segment into sub-segments, and evaluate the value of each of the coefficients
			//	found previously in each sub segment
			List<SegmentNotesInfo> currentSegmentContent = frequencyFinder.EvaluateCoefficientsValue(detectedNotes, activeBuffer[0], (int)currentAnalyzerSample, (int)currentAnalyzerSample + _windowSizeInSamples - 1, segmentId++, preferences, _numberOfSubSegments);
			
			//	Collect all segments together
			fileInfo.addAll(currentSegmentContent);
			//fileInfo.add(new SegmentNotesInfo(segmentId++, currentAnalyzerSample, currentAnalyzerSample + _windowSizeInSamples - 1, currentSegmentContent));
			
			currentAnalyzerSample += _advanceFactorInSamples;
			
			System.out.println("============================");
			System.out.println("\tSegment #: "+iterationIndex++);
			System.out.println("============================");
			
			//for(int cycleLength : detectedCycles)
			//{
			//	System.out.println("Found frequency: " + (double)wavLoader.getSampleFrequency() / cycleLength);
			//}
			
			//	For DEBUG, stop after ? cycles
			
			//if(iterationIndex >= 7)
			//{
				//break;
			//}
		}
		keepRunning = false;
		return fileInfo;
	}

	private double[][] getOldBuffer() {
		return currentUsedBuffer ? samplesBuffer2 : samplesBuffer1;
	}

	private double[][] getNewBuffer() {
		return currentUsedBuffer ? samplesBuffer1 : samplesBuffer2;
	}
	
	public long getSampleRate() {
		return wavLoader == null ? 0 : wavLoader.getSampleFrequency();
	}

	public double getWindowLength() {
		return _windowSizeInSeconds / _numberOfSubSegments;
	}

	public long getNumberOfFramesPerWindow() {
		return _windowSizeInSamples;
	}

//	@Override
//	public void run() {
//		_isReady = false;
//		_result = RunAnalysis(_fileName);
//		_isReady = true;
//		
//	}
//	
//	public List<SegmentNotesInfo> GetResults()
//	{
//		return _isReady ? _result : null;
//	}
//
//	public boolean isReady() {
//		return _isReady;
//	}

}
